#include <QDebug>
#include <QSqlQuery>
#include <QSqlError>
#include <QToolTip>
#include <QMessageBox>
#include "AdapterTelnet.h"
#include "ui_guiofsink.h"
#include "db.h"
#include "threadtelnet.h"
#include "macro.h"
#include "db.h"

AdapterTelnet::AdapterTelnet(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::GUIOfSink)
{
    ui->setupUi(this);
    dbo=new DatabaseOperation(this);
    if(dbo->createConnection(DB_NAME,DB_HOST,
         DB_USERNAME,DB_PWD,QString(DB_CON_NAME_ADAPTER_TELNET)))
    {
     }
      else
    {
        QMessageBox::critical(this,tr("错误"),tr("数据库连接失败"));
        exit(2);
    }

    listNodeNotWorkPressed=false;
    listNodeToConnectPressed=false;
    Qt::WindowFlags flags =Qt::Widget|Qt::CustomizeWindowHint|Qt::WindowMinMaxButtonsHint;
    setWindowFlags(flags);

    connect(this,SIGNAL(sigNodeFromNotWorkList2ToConnectList(QByteArray,qint64)),this,
            SLOT(slotNodeFromNotWorkList2ToConnectList(QByteArray,qint64)));
    connect(this,SIGNAL(sigNodeFromToConnectList2NotWorkList(QByteArray,qint64)),this,
             SLOT(slotNodeFromToConnectList2NotWorkList(QByteArray,qint64)));
    connect(this,SIGNAL(sigShowInfoToTextEdit(QByteArray)),this,SLOT(slotShowInfoToTextEdit(QByteArray)));

    udpSocket.bind(Adapter_Telnet_UDPPort);
    connect(&udpSocket,SIGNAL(readyRead()),
            this,SLOT(slotProcessUDPDatagrams()));

    ui->pushButtonOK->click();
    qint64 rowCount=ui->listWidgetNodesNotWork->count();
    for(int n=0;n<rowCount;n++)
    {
        QListWidgetItem *lwi=ui->listWidgetNodesNotWork->takeItem(0);
        on_listWidgetNodesNotWork_itemPressed(lwi);
        ui->listWidgetNodesToConnect->addItem(lwi->text());
        on_listWidgetNodesToConnect_itemChanged(lwi);
        ui->listWidgetNodesNotWork->removeItemWidget(lwi);
        delete lwi;
    }
}

AdapterTelnet::~AdapterTelnet()
{
    delete ui;
}

void AdapterTelnet::on_pushButtonOK_clicked()
{    
    ui->pushButtonOK->setEnabled(false);
    QList <QByteArray> listUpdateIP;
    ui->listWidgetNodesNotWork->clear();

    listNodeIPNotWork.clear();

    QSqlQuery tempQuery(QSqlDatabase::database(dbo->dbConnectionName));

    if(tempQuery.exec("select distinct UpdateIP from nodeaddressmap where "
            "NodeType like '%"+ui->comboBoxNodeType->currentText()+"%' and ProtocHead='"+
            ui->comboBoxProtocal->currentText()+"' and UpdateIP!='0.0.0.0'"))
    {
        while(tempQuery.next())
        {
            QByteArray updateIP=tempQuery.value(0).toByteArray();
            if(regExpValidIP->exactMatch(updateIP)&&!listNodeIPNotWork.contains(updateIP)&&!listNodeIPToConnect.contains(updateIP))
            {
                ui->listWidgetNodesNotWork->addItem(updateIP);
                listNodeIPNotWork.append(updateIP);
                listUpdateIP.append(updateIP);
            }
        }
    }
    else
        emit sigShowInfoToTextEdit("ERROR in on_pushButtonOK_clicked():"+tempQuery.lastError().text().toUtf8());

    for(int index=0;index<listUpdateIP.size();index++)
    {
        if(tempQuery.exec("select nodename,nodemacaddress from nodeaddressmap where "
                                        " UpdateIP='"+listUpdateIP.at(index)+"'"))
        {
            QByteArray nodeNameAll;
            qint64 subAddr(0); //低7字节的地址（消除虚拟节点影响）
            qint64 mask= Q_INT64_C(0x00ffffffffffffff);

            if(tempQuery.next())
            {
                nodeNameAll.append(tempQuery.value(0).toByteArray()+'\n');
                subAddr=tempQuery.value(1).toLongLong()&mask;
            }
            while(tempQuery.next())
                nodeNameAll.append(tempQuery.value(0).toByteArray()+'\n');

            mapIPName.insert(listUpdateIP.at(index),nodeNameAll);
            mapIPAddr.insert(listUpdateIP.at(index),subAddr);
        }
        else
            emit sigShowInfoToTextEdit("ERROR in on_pushButtonOK_clicked():"+tempQuery.lastError().text().toUtf8());
    }

}

void AdapterTelnet::on_listWidgetNodesToConnect_itemChanged(QListWidgetItem *item)
{
    if(item->text().isEmpty()&&listNodeToConnectPressed) //被移除
    {
        emit sigNodeFromToConnectList2NotWorkList(textPressedInListToConnect,
                mapIPAddr.value(textPressedInListToConnect));
        listNodeIPToConnect.removeOne(textPressedInListToConnect);
        listNodeIPNotWork.append(textPressedInListToConnect);
        listNodeNotWorkPressed=false;
        listNodeToConnectPressed=false;
        item->setHidden(true);
        //ui->listWidgetNodesToConnect->takeItem(ui->listWidgetNodesToConnect->currentRow());
    }
    else if(!item->text().isEmpty()&&listNodeNotWorkPressed) //移入
    {
        emit sigNodeFromNotWorkList2ToConnectList(item->text().toUtf8(),
                mapIPAddr.value(item->text().toUtf8()));
        listNodeIPNotWork.removeOne(item->text().toUtf8());
        listNodeIPToConnect.append(item->text().toUtf8());
        listNodeNotWorkPressed=false;
        listNodeToConnectPressed=false;        
    }
}


void AdapterTelnet::on_listWidgetNodesToConnect_itemPressed(QListWidgetItem *item)
{
    listNodeToConnectPressed=true;
    textPressedInListToConnect=item->text().toUtf8();
}

void AdapterTelnet::on_listWidgetNodesNotWork_itemClicked(QListWidgetItem *item)
{
    listNodeNotWorkPressed=false;
    if(!item->text().isEmpty())
        QToolTip::showText(QCursor::pos(),mapIPName.value(item->text().toUtf8()));
}

void AdapterTelnet::on_listWidgetNodesNotWork_itemPressed(QListWidgetItem *item)
{
    listNodeNotWorkPressed=true;
}

void AdapterTelnet::on_listWidgetNodesToConnect_itemClicked(QListWidgetItem *item)
{
    listNodeToConnectPressed=false;
    if(!item->text().isEmpty())
        QToolTip::showText(QCursor::pos(),mapIPName.value(item->text().toUtf8()));
}

void AdapterTelnet::on_listWidgetNodesConnected_itemClicked(QListWidgetItem *item)
{
    if(!item->text().isEmpty())
        QToolTip::showText(QCursor::pos(),mapIPName.value(item->text().toUtf8()));
}

void AdapterTelnet::on_listWidgetNodesNotWork_itemChanged(QListWidgetItem *item)
{
    if(item->text().isEmpty())
    {
        item->setHidden(true);
    }
}

void AdapterTelnet::slotNodeFromNotWorkList2ToConnectList(QByteArray nodeIP, qint64 subAddr)
{
    ThreadTelnet *newThread=new ThreadTelnet(ui->spinBoxPort->value(),nodeIP,subAddr,this);
    newThread->start();
    connect(newThread,SIGNAL(sigShowInfoToErrorTextEdit(QByteArray)),this,SLOT(slotShowInfoToErrorTextEdit(QByteArray)));
    //下面是为了处理用户把节点从toConnect区拖到notwork区，此时销毁对应的线程
    connect(this,SIGNAL(sigExitThread(QByteArray,qint64)),newThread,SLOT(slotExitThread(QByteArray,qint64)));
    //下面是为了处理连接断开，尝试多次后失败的情况。此时将to connect区中的节点移到notwork区
    connect(newThread,SIGNAL(sigThreadDestroyed(QByteArray,qint64)),this,SLOT(slotThreadDestroyed(QByteArray,qint64)));
    connect(newThread,SIGNAL(sigTelnetDisconnectedTemporarily(QByteArray,qint64)),this,
                    SLOT(slotTelnetDisconnectedTemporarily(QByteArray,qint64)));
    connect(newThread,SIGNAL(sigTelnetConnected(QByteArray,qint64)),this,
            SLOT(slotTelnetConnected(QByteArray,qint64)));
    connect(newThread,SIGNAL(sigShowInfoToTextEdit(QByteArray)),this,
             SLOT(slotShowInfoToTextEdit(QByteArray)));
    connect(this,SIGNAL(sigPacketToNodes(QByteArray,qint64)),newThread,
            SIGNAL(sigPacketToNodes(QByteArray,qint64)));
    connect(newThread,SIGNAL(sigPacketReady(QByteArray)),this,SLOT(slotPacketReady(QByteArray)));
}

void AdapterTelnet::slotNodeFromToConnectList2NotWorkList(QByteArray nodeIP, qint64 subAddr)
{
    emit sigExitThread(nodeIP,subAddr); //退出并销毁线程
}

//从toconnect到notwork
void AdapterTelnet::slotThreadDestroyed(QByteArray nodeIP, qint64 subAddr)
{
    QList<QListWidgetItem *> listWI=ui->listWidgetNodesToConnect->findItems(nodeIP,Qt::MatchFixedString);
    if(listWI.isEmpty())
        emit sigShowInfoToTextEdit("Warning: in slotThreadDestroyed. Can't find item in toconnect nodes with IP:"+nodeIP+
                                   " If you draged a node from toconnect to notwork, then it's OK");
    else
    {
        if(listNodeIPNotWork.contains(listWI.at(0)->text().toUtf8()))
            return;
        ui->listWidgetNodesNotWork->addItem(listWI.at(0)->text());
        listWI.at(0)->setHidden(true);
        listNodeIPNotWork.append(listWI.at(0)->text().toUtf8());
        ui->listWidgetNodesToConnect->removeItemWidget(listWI.at(0));
    }
}

//从connected到toconnect
void AdapterTelnet::slotTelnetDisconnectedTemporarily(QByteArray nodeIP, qint64 subAddr)
{
    QList<QListWidgetItem *> listWI=ui->listWidgetNodesConnected->findItems(nodeIP,Qt::MatchFixedString);
    if(listWI.isEmpty())
        emit sigShowInfoToTextEdit("Warning: in slotTelnetDisconnectedTemporarily. Can't find item in toconnect nodes with IP:"+nodeIP);
    else
    {
        ui->listWidgetNodesToConnect->addItem(listWI.at(0)->text());
        listNodeIPToConnect.append(listWI.at(0)->text().toUtf8());
        listWI.at(0)->setHidden(true);
        ui->listWidgetNodesConnected->removeItemWidget(listWI.at(0));
    }
}

//从toconnect到connected
void AdapterTelnet::slotTelnetConnected(QByteArray nodeIP, qint64 subAddr)
{
    QList<QListWidgetItem *> listWI=ui->listWidgetNodesToConnect->findItems(nodeIP,Qt::MatchFixedString);
    if(listWI.isEmpty())
        emit sigShowInfoToTextEdit("Warning: in slotTelnetConnected. Can't find item in toconnect nodes with IP:"+nodeIP);
    else
    {
        ui->listWidgetNodesConnected->addItem(listWI.at(0)->text());
        listWI.at(0)->setHidden(true);
        listNodeIPToConnect.removeOne(listWI.at(0)->text().toUtf8());
        ui->listWidgetNodesToConnect->removeItemWidget(listWI.at(0));
    }
}

void AdapterTelnet::slotShowInfoToTextEdit(QByteArray text)
{
    if(ui->checkBoxShowInfo->isChecked())
        ui->plainTextEdit->appendPlainText(text);
}

void AdapterTelnet::slotShowInfoToErrorTextEdit(QByteArray text)
{
    ui->plainTextEditErrorLog->appendPlainText("\n"+text);
}

void AdapterTelnet::on_pushButtonClearTextEdit_clicked()
{
    ui->plainTextEdit->clear();
}

void AdapterTelnet::slotPacketReady(QByteArray packet)
{
    //下面信号是告诉物联网服务器WeatherStation，telnet 适配器的IP和UDP端口为多少，节点上传的数据包是什么。
    //该信号将与物联网服务器的analyzeGWDatagram槽连接，效果等同于：该适配器通过UDP端口向weatherstation发送数据包
    emit sigPacketNodeToServer(packet,QHostAddress(aServerIP),Adapter_Telnet_UDPPort,false);
    emit sigShowInfoToTextEdit(packet.toHex()+" is sent to IOTServer");
}

void AdapterTelnet::slotProcessUDPDatagrams()
{
    bool ok;
    QByteArray datagram;
    emit sigShowInfoToTextEdit("UDP Pakcet come");
    do
    {
       datagram.clear();
      datagram.resize(udpSocket.pendingDatagramSize());
      udpSocket.readDatagram(datagram.data(),datagram.size()); //读数据包

       QByteArray dataHex=datagram.toHex();
       if(dataHex.toUpper().startsWith("AA"))
       {
           if(datagram.size()>Dest_Pos+Dest_Size-1)
           {
               qint64 subAddr=datagram.mid(Dest_Pos,Dest_Size).toHex().toLongLong(&ok,16);
               qint64 mask= Q_INT64_C(0x00ffffffffffffff);

               subAddr=subAddr&mask;
               //emit sigShowInfoToTextEdit("subAddr is "+QByteArray::number(subAddr));
                emit sigPacketToNodes(datagram,subAddr);
           }
           else
               continue;
       }
        else if(datagram.startsWith("INF")||datagram.startsWith("WAR")||datagram.startsWith("ERR"))
            emit sigShowInfoToTextEdit(datagram);

       qApp->processEvents(QEventLoop::AllEvents,2000);

    }while (udpSocket.hasPendingDatagrams());
}

void AdapterTelnet::on_pushButtonAbout_clicked()
{
    QByteArray text="单击第一个框和第二个框中的节点IP，会显示节点名。\n"
            "将节点IP从第一个框拖入第二个框，程序会努力去建立与第二个框中的节点的连接。\n"
            "如果多次重连后仍然失败，则程序会将第二个框中的节点移到第一个框。节点连接成功后，会进入第三个框。\n"
            "节点连接断开时，会从第三个框进入第二个框。\n"
            "《网关节点接口协议和架构说明书V2.7》compliant \n"
            "要求：将节点的IP地址写入数据库NodeAddressMap表的UpdateIP项。\n"
            "该程序考虑了虚拟节点。\n"
            "------Wu Lin";
    QMessageBox::information(this,"About",tr(text));
}


void AdapterTelnet::on_comboBoxNodeType_currentIndexChanged(const QString &arg1)
{
    ui->pushButtonOK->setEnabled(true);
}



void AdapterTelnet::on_pushButtonExit_clicked()
{
    exit(0);
}

void AdapterTelnet::on_pushButtonHide_clicked()
{
    hide();
}


void AdapterTelnet::on_spinBoxPort_valueChanged(int arg1)
{
    ui->pushButtonOK->setEnabled(true);
}
